#include "RC6Decoder.h"


#define RC6_PREFIX_RC6    0x000FC950
#define RC6_PREFIX_RC6A   0x000FCA90
#define MCE_CUSTOMER_MICROSOFT    0x800F
#define MCE_TOGGLE_BIT    0x8000
#define MCE_TOGGLE_MASK   0x7FFF


typedef struct {	
	const char *name;
	int key;	
} RemoteNames ;

static RemoteNames remoteNames [] = {
    { "TVPWR",	0x7b9a },
    { "BLUE",	0x7ba1 },
    { "YELLOW",	0x7ba2 },
    { "GREEN",	0x7ba3 },
    { "RED",		0x7ba4 },
    { "TELETEXT",0x7ba5 },
    { "RADIO",	0x7baf },
    { "PRINT",	0x7bb1 },
    { "VIDEOS",	0x7bb5 },
    { "PICTURES",0x7bb6 },
    { "RECTV",	0x7bb7 },
    { "MUSIC",	0x7bb8 },
    { "TV",		0x7bb9 },
    { "GUIDE",	0x7bd9 },
    { "LIVETV",	0x7bda },
    { "DVDMENU",	0x7bdb },
    { "BACK",	0x7bdc },
    { "OK",		0x7bdd },
    { "RIGHT",	0x7bde },
    { "LEFT",	0x7bdf },
    { "DOWN",	0x7be0 },
    { "UP",		0x7be1 },
    { "STAR",	0x7be2 },
    { "POUND",	0x7be3 },
    { "REPLAY",	0x7be4 },
    { "SKIP",	0x7be5 },
    { "STOP",	0x7be6 },
    { "PAUSE",	0x7be7 },
    { "RECORD",	0x7be8 },
    { "PLAY",	0x7be9 },
    { "REWIND",	0x7bea },
    { "FORWARD",	0x7beb },
    { "CHDOWN",	0x7bec },
    { "CHUP",	0x7bed },
    { "VOLDOWN",	0x7bee },
    { "VOLUP",	0x7bef },
    { "DETAILS",	0x7bf0 },
    { "MUTE",	0x7bf1 },
    { "EHOME",	0x7bf2 },
    { "PCPWR",	0x7bf3 },
    { "ENTER",	0x7bf4 },
    { "ESCAPE",	0x7bf5 },
    { "9",		0x7bf6 },
    { "8",		0x7bf7 },
    { "7",		0x7bf8 },
    { "6",		0x7bf9 },
    { "5",		0x7bfa },
    { "4",		0x7bfb },
    { "3",		0x7bfc },
    { "2",		0x7bfd },
    { "1",		0x7bfe },
    { "0",		0x7bff },
    { 0,0}
};


RC6Decoder::RC6Decoder( ILoggerPtr logger, PublisherPtr publisher ) : BaseIrDecoder(logger, publisher)
{

}

bool RC6Decoder::process ( unsigned long * Data, size_t DataCount )
{
    DWORD i;

    if (!Data)
    {    
        m_DetectState = DETECT_HEADER_PULSE;
        return false;
    }

    for (i = 0 ; i < DataCount ; i++)
    {
        DWORD Duration = Data[i] & PULSE_MASK;
        BOOL Pulse = ((Data[i] & PULSE_BIT) != 0);
        BOOL Ignored = TRUE;
     
        switch(m_DetectState)
        {
        case DETECT_HEADER_PULSE:
            if (Pulse && (Duration >= 2600) && (Duration <= 3300))
            {
                m_DetectState = DETECT_HEADER_SPACE;
                m_Header = 0x000FC000;
                m_Bit = 12;
                m_HalfBit = 0;
                m_Code = 0;
                m_LongPulse = FALSE;
                m_LongSpace = FALSE;
                Ignored = FALSE;
                
            }
            break;
        case DETECT_HEADER_SPACE:
            if (!Pulse && (Duration >= 750) && (Duration <= 1000))
            {
                Ignored = FALSE;
                m_DetectState = DETECT_PRE_DATA;
                
            }
            break;
        case DETECT_PRE_DATA:
            if (Pulse)
            {
                if ((Duration >= 300) && (Duration <= 600))
                {
                    Ignored = FALSE;
                    if (m_Bit) m_Header |= 1 << --m_Bit;
                    
                }
                else if ((Duration >= 750) && (Duration <= 1000))
                {
                    Ignored = FALSE;
                    if (m_Bit) m_Header |= 1 << --m_Bit;
                    if (m_Bit) m_Header |= 1 << --m_Bit;
                    
                }
                else if ((Duration >= 1200) && (Duration <= 1600))
                {
                    Ignored = FALSE;
                    if (m_Bit) m_Header |= 1 << --m_Bit;
                    if (m_Bit) m_Header |= 1 << --m_Bit;
                    if (m_Bit)
                    {
                        m_Header |= 1 << --m_Bit;
                    }
                    else
                    {
                        m_HalfBit = 1;
                        m_LongPulse = TRUE;
                    }
                    
                }
                else
                {
                    // DebugRC6("RC6 Error Bit %d %s %d\n", m_Bit, Pulse ? "Pulse" : "Space", Duration);
                }
            }
            else
            {
                if ((Duration >= 300) && (Duration <= 600))
                {
                    Ignored = FALSE;
                    m_Bit--;
                    
                }
                else if ((Duration >= 750) && (Duration <= 1000))
                {
                    Ignored = FALSE;
                    if (m_Bit > 2) m_Bit -= 2; else m_Bit = 0;
                    
                }
                else if ((Duration >= 1200) && (Duration <= 1600))
                {
                    Ignored = FALSE;
                    if (m_Bit >= 3)
                    {
                        m_Bit -= 3;
                    }
                    else
                    {
                        m_HalfBit = 1;
                        m_LongPulse = TRUE;
                        m_Bit = 0;
                    }
                    
                }
                else
                {
                    // DebugRC6("RC6 Error Bit %d %s %d\n", m_Bit, Pulse ? "Pulse" : "Space", Duration);
                }
            }
            if ((Ignored == FALSE) && (m_Bit == 0))
            {
                m_DetectState = DETECT_DATA;
                
            }
            break;
        case DETECT_DATA:
            if ((m_HalfBit % 2) == 0)
            {
               
               if (Pulse && (Duration >= 300) && (Duration <= 600))
                {
                    Ignored = FALSE;
                    m_LongPulse = TRUE;
                    m_HalfBit++;
               
                }
                else if (!Pulse && (Duration >= 300) && (Duration <= 600))
                {
                    Ignored = FALSE;
                    m_LongSpace = TRUE;
                    m_HalfBit++;
               
                }
                else if (!Pulse && Duration > 4000)
                {
                    if (m_Bit == 16 || m_Bit == 20 || m_Bit == 24 || m_Bit == 32)
                        m_DetectState = DETECT_KEYCODE;
                }
                break;
            }
            if (m_LongPulse)
            {
               
                m_LongPulse = FALSE;
                if (Pulse)
                {
                    
                    break;
                }
                if ((Duration >= 750) && (Duration <= 1000))
                {
                    Ignored = FALSE;
                    m_Bit++;
                    m_Code = m_Code << 1;
                    m_LongSpace = TRUE;
                    m_HalfBit += 2;
                    
                }
                else if ((Duration >= 300) && (Duration <= 600))
                {
                    Ignored = FALSE;
                    m_Bit++;
                    m_Code = m_Code << 1;
                    m_HalfBit++;
                    
                }
                else if (Duration > 4000)
                {
                    Ignored = FALSE;
                    m_Bit++;
                    m_Code = m_Code << 1;
                    m_HalfBit++;
                    if (m_Bit == 16 || m_Bit == 20 || m_Bit == 24 || m_Bit == 32)
                        m_DetectState = DETECT_KEYCODE;
                    
                }
            }
            else if (m_LongSpace)
            {
               
                m_LongSpace = FALSE;
                if (!Pulse)
                {
                    break;
                }
                if ((Duration >= 750) && (Duration <= 1000))
                {
                    Ignored = FALSE;
                    m_Bit++;
                    m_Code = m_Code << 1;
                    m_Code |= 1;
                    m_LongPulse = TRUE;
                    m_HalfBit += 2;
                    
                }
                else if ((Duration >= 300) && (Duration <= 600))
                {
                    Ignored = FALSE;
                    m_Bit++;
                    m_Code = m_Code << 1;
                    m_Code |= 1;
                    m_HalfBit++;
                    
                }
                else if (Duration > 4000)
                {
                    if (m_Bit == 16 || m_Bit == 20 || m_Bit == 24 || m_Bit == 32)
                        m_DetectState = DETECT_KEYCODE;
                    
                }
            }
            break;
        }
        if (m_DetectState == DETECT_KEYCODE)
        {
            
            BOOL IsValid = FALSE;
            if (m_LastCode != m_Code)
            {
                IsValid = TRUE;
                m_LastCode = m_Code;
                m_LastTime = GetTickCount();
                m_RepeatCount = 0;
            }
            else if (m_KbdFirstRepeat &&
                (m_LastTime + m_KbdFirstRepeat < GetTickCount()))
            {
                IsValid = TRUE;
                m_LastTime = GetTickCount() + m_KbdNextRepeats - m_KbdFirstRepeat;
                m_RepeatCount++;
            }

            m_DetectState = DETECT_HEADER_PULSE;

            if (IsValid)
            {
                if ((~m_Code >> 16) == MCE_CUSTOMER_MICROSOFT)
                {
                    m_Code &= MCE_TOGGLE_MASK;
                }
                
                //PostMessage(hWndRegistered, WM_USER,  (m_RepeatCount << 16) | m_Code);
				// MceIrDecoded( (m_RepeatCount << 16) | m_Code);
				int i = 0 ;
				DWORD key = m_Code;
				const char * name = "?";

				while ( remoteNames[i].name )
				{
					if ( remoteNames[i].key == key ) 
					{
						name = remoteNames[i].name;
						break;
					}
					i++;
				}
				m_Publisher->call(ID_RC6_KEYCODE, key, m_RepeatCount, name );
				return true;
            }
        }
        if (Ignored && (m_DetectState != DETECT_HEADER_PULSE))
        {
            m_DetectState = DETECT_HEADER_PULSE;            
        }
    }
	return false;
}